const { 
  TYPE_OBJECT,
  _typeOf,
  _deepClone,
  _isObjEqual
} = require('./util');

let $state =  Symbol('$state'),
    $openPart =  Symbol('$openPart'),
    $behavior =  Symbol('$behavior'),
    $methods =  Symbol('$methods'),
    $pageLife =  Symbol('$pageLife'),
    $pageListener =  Symbol('$pageListener'),
    $nonWritable =  Symbol('$nonWritable'),
    $stack =  Symbol('$stack'),
    $debug =  Symbol('$debug');

class ProxyStore{
  constructor(opts){
    // 初始化数据
    this.initData(opts);
    // 初始化页面周期数组
    this.initPageLife();
    // 重写 Page 对象
    this.rewritePage();
    // 重写 Component 对象
    this.rewriteComponent();
  }
  initData(opts){
    const { 
      openPart = false,
      behavior,
      methods = {},
      pageLisener = {},
      pageListener,
      nonWritable = false,
      debug = true,
    } = opts;
    if(_typeOf(opts.state) === TYPE_OBJECT){
      this[$state] = _deepClone(opts.state);
    }
    this[$openPart] = openPart;
    this[$behavior] = behavior;
    this[$methods] = methods;
    this[$pageListener] = pageListener || pageLisener;
    this[$nonWritable] = nonWritable;
    this[$debug] = debug;
    this[$stack] = [];
  }
  initPageLife(){
    this[$pageLife] = [
      "data",
      "onLoad",
      "onShow",
      "onReady",
      "onHide",
      "onUnload",
      "onPullDownRefresh",
      "onReachBottom",
      "onShareAppMessage",
      "onPageScroll",
      "onTabItemTap",
    ]
  }
  created(page){
    !this[$stack].some(cur => cur === page) && this[$stack].push(page);
    page.watch && this.watch(page)
    if(!_isObjEqual(page.data.$state, this[$state])){
      page.setData({$state: this[$state]})
    }
  }
  destroy(page){
    let index = this[$stack].findIndex(cur => cur === page);
    ~index && this[$stack].splice(index, 1);
  }
  watch(page){
    page.data = new Proxy(page.data,{
      set(target, key, value, receiver){
        page.watch && page.watch[key] && page.watch[key].call(page, value);
        return Reflect.set(target, key, value, receiver);
      },
      get(target, key, receiver){
        return Reflect.get(target, key, receiver);
      }
    })
  }
  rewritePage(){
    const _Page = Page;
    const _this = this;
    App.Page = (options = {}, ...args) => {
      const { onLoad, onUnload } = options;
      options = {
        ...options,
        data: {
          ...(options.data || {}),
          $state: _this[$state]
        },
        ...(_this[$methods] || {}),
        onLoad(opts){
          _this.created(this)
          onLoad && onLoad.call(this,opts)
        },
        onUnload(){
          _this.destroy(this)
          onUnload && onUnload.call(this)
        }
      }
      Object.keys(_this[$pageListener]).forEach(key => {
        if(typeof _this[$pageListener][key] === "function" && _this[$pageLife].some((item) => item === key)){
          const lifeName = options[key];
          options = {
            ...options,
            [key](opts){
              let globalValue = _this[$pageListener][key].call(this, opts);
              let pageValue = lifeName && lifeName.call(this, opts);
              return pageValue || globalValue;
            }
          }
        }
      })
      _Page(options, ...args)
    }
    if (!this[$nonWritable]) {
      try {
        Page = App.Page;
      } catch (e) {}
    }
  }
  rewriteComponent(){
    const _Component = Component;
    const _this = this;
    App.Component = (options = {}, ...args) => {
      const { lifetimes = {} } = options;
      let attached = lifetimes.attached || options.attached,
          detached = lifetimes.detached || options.detached;
      options = {
        ...options,
        data: {
          ...(options.data || {}),
          $state: _this[$state]
        }
      }
      Object.keys(_this[$methods]).forEach(key => {
        if(typeof _this[$methods][key] === "function" && !_this[$pageLife].some((item) => item === key)){
          options.methods || (options.methods = {})
          const lifeName = options.methods[key];
          options.methods[key] = function(opts){
            _this[$methods][key].call(this, opts);
            lifeName && lifeName.call(this,opts);
          }
        }
      })
      let attachednew = function(){
        _this.created(this)
        attached && attached.call(this)
      }
      let detachednew = function(){
        _this.destroy(this)
        detached && detached.call(this)
      }
      if(options.lifetimes && _typeOf(options.lifetimes) === TYPE_OBJECT){
        options.lifetimes.attached = attachednew;
        options.lifetimes.detached = detachednew;
      } else {
        options.attached = attachednew;
        options.detached = detachednew;
      }
      _Component(options, ...args)
    }
    if (!this[$nonWritable]) {
      try {
        Component = App.Component;
      } catch (e) {}
    }
  }
  getState() {
    return _deepClone(this[$state]);
  }
  setState(obj, fn = () => {}) {
    if (_typeOf(obj) !== TYPE_OBJECT) throw new Error("setState的第一个参数须为object!");
    let prev = this[$state];
    let current = {
      ..._deepClone(prev),
      ..._deepClone(obj)
    };
    this[$state] = current;
    if(this[$stack].length){
      let props = this[$stack].map(page => {
        return new Promise((resolve,reject) => {
          page.setData({$state: current}, resolve)
        })
      })
      Promise.all(props).then(fn);
    }else{
      fn();
    }
  }
}
module.exports = ProxyStore;